namespace OpenComposite.Inbox
{
	using System;
	using System.ComponentModel;
	using System.Data;
	using System.Diagnostics;
	using System.Drawing;
	using System.IO;
	using System.Reflection;
	using System.ServiceModel;
	using System.Threading;
	using System.Windows.Forms;

	using Microsoft.Win32;

	using OpenComposite.Base;
	using OpenComposite.Base.Structures;
	using OpenComposite.HumanActivity.Contracts;
	using OpenComposite.Inbox.Data;
	using OpenComposite.Inbox.Forms;
	using OpenComposite.Inbox.Forms.SelfService;

	/// <summary>
	/// Summary description for formMain.
	/// </summary>
	public partial class FormMain : System.Windows.Forms.Form
	{
		#region Initialization

		/// <summary>
		/// 
		/// </summary>
		/// <param name="da"></param>
		public FormMain()
		{
			this.RemovedActivityId = -1;
			this.LastSelectedRow = -1;

			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			dgvInbox.AutoGenerateColumns = false;

			m_timer = new System.Windows.Forms.Timer();
			m_timer.Interval = 120000;
			m_timer.Tick += new EventHandler(m_timer_Tick);
			m_timer.Start();
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public int LastSelectedRow { get; set; }

		public int RemovedActivityId { get; set; }

		#endregion Properties

		#region Methods

		public void Init()
		{
			refreshActivityInbox();
		}

		#endregion Methods

		#endregion Public Members

		#region Private Members

		#region Fields

		private DialogResult _haFormResult = DialogResult.None;
		private FormDebugWindow m_debugWindow;
		private string m_lastExceptionTrace = "";
		private string m_sActivityInstanceID = "0";
		private System.Windows.Forms.Timer m_timer;
		private ListViewTraceListener m_traceListener;

		#endregion Fields

		#region Event Handlers

		/// <summary>
		/// Opens a Human Activity Form
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>       
		private void FormWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			ActivityItem ai = e.Argument as ActivityItem;
			if ( ai == null || !ai.ActivityId.HasValue ) {
				cancelFormWorker(e, ai, "Invalid Activity Item.");
				return; // EXIT
			}
			if ( ai.HumanActivityUri == "" ) {
				cancelFormWorker(e, ai, "Missing Human Activity Service Url. Please check the Human Activity Configuration.");
				return; // EXIT
			}
			Object obj = null;
			Type t = null;
			int userId = Global.CurrentUser.UserID.Value;
			bool ok = false;
			int retryCount = 0;
			FormInfo formInfo = null;
			IHumanActivityFormService formSvc = null;
			string message = null;
			do {
				try {
					FormWorker.ReportProgress(0, "Opening of the Activity Item...");

					formSvc = ChannelFactory<IHumanActivityFormService>.CreateChannel(
						new NetTcpBinding("NetTcpBinding_IHumanActivityFormService"),
						new EndpointAddress(ai.HumanActivityUri + "/Form"));

					ok = formSvc.StartActivity(ai.ActivityId.Value, userId);
					if ( !ok && !string.IsNullOrEmpty(message) ) {
						cancelFormWorker(e, ai, message);
						return; // EXIT
					}
				} catch ( Exception ex ) {
					Debug.WriteLine(ex.ToString());
					retryCount++;
					ok = false;
					if ( retryCount > 5 ) {
						cancelFormWorker(e, ai, new Exception("Error while opening Activity Item.", ex));
						return; // EXIT
					}
				}
			} while ( !ok );

			formInfo = formSvc.GetFormInfo();
			if ( formInfo == null ) {
				cancelFormWorker(e, ai, "No valid Form Informations available.");
				return; // EXIT
			}
			if ( formInfo.FormName == "" ) {
				cancelFormWorker(e, ai, "Missing Human Activity Form Name. Please check Human Activity Process Configuration.");
				return; // EXIT
			}

			if ( formInfo.AssemblyName == "" ) {
				cancelFormWorker(e, ai, "Missing Human Activity Form Assembly Name. Please check Human Activity Process Configuration.");
				return; // EXIT
			}
			FormWorker.ReportProgress(25,
				String.Format("Downloading Form Assembly: {0}...", formInfo.AssemblyName));
			byte[] dll = formSvc.GetForm();

			FormLoader load = new FormLoader();
			try {
				// load and show form
				load.FormService = formSvc;

				string path = Application.CommonAppDataPath;

				Assembly asm = load.GetAssembly(path, formInfo.AssemblyName);

				FormWorker.ReportProgress(55,
					String.Format("Loading Form {0} from Assembly: {1}",
						formInfo.FormName, formInfo.AssemblyName));

				obj = load.GetObjectFromAssembly(asm, formInfo.AssemblyName, formInfo.FormName);

				FormWorker.ReportProgress(70, "Initializing Form Instance");

				t = obj.GetType();

				t.InvokeMember("ActivityItem",
					BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance,
					null, obj, new object[] { ai });

				t.InvokeMember("CurrentUser",
					BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance,
					null, obj, new object[] { Global.CurrentUser });

				t.InvokeMember("BGworker",
					BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance,
					null, obj, new object[] { FormWorker });

			} catch ( Exception ex ) {
				Debug.WriteLine(ex.ToString());
				cancelFormWorker(e, ai, new Exception("Couldn't get form from Human Activity Service.", ex));
				return; // EXIT
			}

			try {
				FormWorker.ReportProgress(100, "Showing Dialog...");

				object o1 =
					t.InvokeMember("ShowDialog",
					BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance,
					null, obj, null);
				_haFormResult = (DialogResult)o1;

				PropertyInfo pi = t.GetProperty("NewActivityStatus");
				if ( pi != null ) {
					object oNewStatus = pi.GetValue(obj, null);
					if ( oNewStatus != null && oNewStatus is ActivityStatus ) {
						ActivityStatus newStatus = (ActivityStatus)oNewStatus;
						switch ( newStatus ) {
							case ActivityStatus.Aborted:
								// TODO: abort activity
								break;
							case ActivityStatus.Completed:
								break;
							case ActivityStatus.Faulted:
								// TODO: fault activity
								break;
							case ActivityStatus.InProgress:
								break;
							case ActivityStatus.NotStarted:
								formSvc.CancelActivity(ai.ActivityId.Value, Global.CurrentUser.UserID.Value);
								break;
							default:
								formSvc.CancelActivity(ai.ActivityId.Value, Global.CurrentUser.UserID.Value);
								break;
						}
					}
				}
				// Check For Error Messages in the Form
				pi = t.GetProperty("ErrorMessage");

				if ( pi != null ) {
					Exception ex = (Exception)pi.GetValue(obj, null);
					if ( ex != null && ex.Message != "" ) throw ex;
				}
				IDisposable form = obj as IDisposable;
				if ( form != null ) form.Dispose();
			} catch ( Exception ex ) {
				// Check For Error Messages in the Form
				cancelFormWorker(e, ai, ex);
				return; // EXIT
			}

			e.Result = ai;
		}

		private void FormWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			Exception ex = e.UserState as Exception;
			string stacktrace = "";

			if ( ex != null ) {
				statusStrip1.Items[2].Text = ex.Message;
				statusStrip1.Items[2].Visible = true;
				statusStrip1.Items[3].Visible = true;
				statusStrip1.Items[1].Visible = false;
				statusStrip1.Items[0].Visible = false;

				m_lastExceptionTrace = ex.Message + Environment.NewLine + Environment.NewLine;

				if ( ex.InnerException != null && ex.InnerException.InnerException != null &&
					 ex.InnerException.InnerException.StackTrace != null ) {
					stacktrace = ex.InnerException.InnerException.StackTrace;
				} else {
					if ( ex.InnerException != null && ex.InnerException.StackTrace != null ) {
						stacktrace = ex.InnerException.StackTrace;
					} else if ( ex.StackTrace != null ) {
						stacktrace = ex.StackTrace;
					}
				}
				if ( !string.IsNullOrEmpty(stacktrace) )
					m_lastExceptionTrace += "Stack Trace:" + Environment.NewLine + stacktrace;
			} else {
				statusProgress.Value = 0;
				string InfoText = e.UserState as string;
				for ( int i = 0; i < e.ProgressPercentage; i++ ) {
					statusProgress.Value += 1;
				}
				statusStrip1.Items[1].Text = e.ProgressPercentage.ToString();
				if ( InfoText != null )
					statusStrip1.Items[2].Text = InfoText;
				if ( e.ProgressPercentage == 100 ) toggleStatusBar(false);
			}
		}

		private void FormWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if ( !e.Cancelled ) toggleStatusBar(false);

			statusProgress.Visible = false;

			this.Refresh();
			Application.DoEvents();

			refreshActivityInbox();
		}

		private void dgvInbox_CellClick(object sender, DataGridViewCellEventArgs e)
		{
			if ( e.ColumnIndex == colActivity.Index && e.RowIndex >= 0 ) {
				try {
					ActivityItem ai = dgvInbox.Rows[e.RowIndex].DataBoundItem as ActivityItem;
					LastSelectedRow = e.RowIndex;
					doActivitySelected(ai);
				} catch {

				}

			}
		}

		private void dgvInbox_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
		{
			ActivityItem aitem = dgvInbox.Rows[e.RowIndex].DataBoundItem as ActivityItem;

			if ( e.RowIndex >= 0 && e.ColumnIndex == colActivity.Index ) {
				string val = e.Value == null ? "" : e.Value.ToString();
				int posS = val.IndexOf('>') + 1;
				int posE = val.LastIndexOf('<');
				if ( posS >= 0 && posE >= 0 ) {
					Color col = Color.DarkBlue;
					if ( val.ToLower().Contains("red") ) {
						col = Color.Red;
					}
					val = val.Substring(posS, posE - posS);
					e.Value = val;
					e.CellStyle.ForeColor = col;
					e.FormattingApplied = true;
				}
			} else if ( e.RowIndex >= 0 && e.ColumnIndex == colDueOn.Index &&
					e.Value is DateTime &&
					( aitem.Status == ActivityStatus.InProgress || aitem.Status == ActivityStatus.NotStarted ) ) {
				DateTime dt = (DateTime)e.Value;
				if ( dt < DateTime.Now ) {
					e.Value = dt.ToString("g");
					e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor = Color.Red;
					e.FormattingApplied = true;
				} else if ( dt < DateTime.Now.AddHours(1) ) {
					e.Value = dt.ToString("g");
					e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor = Color.DarkGreen;
					e.FormattingApplied = true;
				}
			} else if ( e.RowIndex >= 0 && ( e.ColumnIndex == colProcInstId.Index ||
											 e.ColumnIndex == colStatus.Index ||
											 e.ColumnIndex == colReceivedOn.Index ) &&
					( aitem.Status == ActivityStatus.InProgress || aitem.Status == ActivityStatus.NotStarted ) ) {
				DataGridViewCell grdInboxVariable = dgvInbox[colDueOn.Index, e.RowIndex];
				if ( grdInboxVariable != null && grdInboxVariable.Value != null ) {
					DateTime dt = (DateTime)grdInboxVariable.Value;
					if ( dt < DateTime.Now ) {
						e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor = Color.Red;
					} else if ( dt < DateTime.Now.AddHours(1) ) {
						e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor = Color.DarkGreen;
					}
				}
			}
		}

		private void formMain_FormClosed(object sender, FormClosedEventArgs e)
		{
			logOut();
		}

		private void formMain_Load(object sender, System.EventArgs e)
		{
			if ( this.DesignMode ) return;

			OpenComposite.Base.Static.RENDERER_VS2005.RoundedEdges = false;
			ToolStripManager.Renderer = OpenComposite.Base.Static.RENDERER_VS2005;

			this.Show();
			this.Refresh();

			toggleStatusBar(false);
			refreshActivityInbox();

			m_debugWindow = new FormDebugWindow();
			this.m_traceListener = new ListViewTraceListener(this.m_debugWindow.DebugListView, "Client Event Log");
			System.Diagnostics.Debug.Listeners.Add(this.m_traceListener);
			System.Diagnostics.Debug.AutoFlush = true;
		}

		private void m_timer_Tick(object sender, EventArgs e)
		{
			m_timer.Stop();
			this.refreshActivityInbox();
			m_timer.Interval = 120000;
			m_timer.Start();
		}

		private void miAbout_Click(object sender, EventArgs e)
		{
			new AboutBox().ShowDialog();
		}

		private void miChangePassword_Click(object sender, System.EventArgs e)
		{
			FormChangePassword frmChangePwd = new FormChangePassword();
			frmChangePwd.ShowDialog();
		}

		private void miLogout_Click(object sender, System.EventArgs e)
		{
			Global.DataAccess.SetOption("user", "");
			logOut();
			this.Hide();
		}

		private void miOptions_Click(object sender, System.EventArgs e)
		{
			FormOptions form = new FormOptions();
			form.ShowDialog();
			this.refreshActivityInbox();
		}

		private void miViewErrorLog_Click(object sender, EventArgs e)
		{
			m_debugWindow.ShowDialog();
		}

		private void statusErrorDetails_Click(object sender, EventArgs e)
		{
			MessageBox.Show(m_lastExceptionTrace, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
		}

		private void statusOK_Click(object sender, EventArgs e)
		{
			toggleStatusBar(false);
		}

		private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			timer1.Stop();
		}

		private void tsbtnLogout_Click(object sender, EventArgs e)
		{
			Global.DataAccess.SetOption("user", "");
			logOut();
			this.Hide();
		}

		private void tsbtnRefresh_Click(object sender, EventArgs e)
		{
			this.refreshActivityInbox();
		}

		#endregion Event Handlers

		#region Methods

		private void cancelFormWorker(DoWorkEventArgs e, ActivityItem ai, string msg)
		{
			FormWorker.ReportProgress(0, msg);
			FormWorker.CancelAsync();
			e.Cancel = true;
			e.Result = ai;
		}

		private void cancelFormWorker(DoWorkEventArgs e, ActivityItem ai, Exception ex)
		{
			FormWorker.ReportProgress(0, ex);
			FormWorker.CancelAsync();
			e.Cancel = true;
			e.Result = ai;
		}

		private void doActivitySelected(ActivityItem actitem)
		{
			toggleStatusBar(true);
			FormWorker.RunWorkerAsync(actitem);
		}

		private void logOut()
		{
			Global.DataAccess.LogOut();
			Global.DataAccess.SetOption("pwd", "");
		}

		private void refreshActivityInbox()
		{
			try {
				Application.DoEvents();
				dgvInbox.DataSource = Global.DataAccess.GetActivityInbox();
			} catch ( Exception ex ) {
				MessageBox.Show("Could not get user inbox items.\n" + ex.Message, "Inbox");
			}
		}

		private void toggleStatusBar(bool onOff)
		{
			statusProgress.Value = 0;
			statusProgress.Visible = onOff;

			statusStrip1.Items[1].Text = "";
			statusStrip1.Items[1].Visible = onOff;

			statusStrip1.Items[2].Text = "";
			statusStrip1.Items[2].Visible = onOff;
			statusStrip1.Items[4].Visible = onOff;

			statusStrip1.Items[3].Visible = false;
		}

		#endregion Methods

		#endregion Private Members
	}
}